//
//  Listing3.m
//  SchmoozingExamples
//
//  Created by garrison on Fri Apr 20 2001.
//  Copyright (c) 2001 Standard Orbit Software, LLC. All rights reserved.
//
//  Permission is granted to use this code for any purpose, at your own risk.
//  No warranties are expressed or implied.

#import <Foundation/Foundation.h>
#import <OmniNetworking/OmniNetworking.h>

/* An Algorithm for an Iterative Server [Comer93]
1) Create a socket and bind it to a well known port number for the server being offered.
2) Establish a listener on the socket for incoming connections.
3) Accept the next connection request on the socket and obtain a new socket for the connection.
4) Repeatedly read the socket for a client request, generate a response, and send the reply back according to the applicationÕs protocol.
5) When finished handling a particular client request, close the connection and go back to step 3, accepting the next connection.
*/

int main (int argc, const char * argv[])
{
    // An Iterative TCP Server
    
    unsigned short serverPort = 1701;
    ONTCPSocket *serverSocket;
    NSAutoreleasePool *mainPool;
    
    mainPool = [[NSAutoreleasePool alloc] init];
    
    NS_DURING {
        serverSocket = [ONTCPSocket tcpSocket];
        // Step 1.  Allocate a socket
        
        [serverSocket startListeningOnLocalPort: serverPort allowingAddressReuse:YES];
        // Step 2.  Start listening on it.
    }
    NS_HANDLER {
        // tcpSocket: could throw ONInternetSocketConnectFailedExceptionName if a
        // a socket can't be allocated.
        // startListeningOnLocalPort throws
        // ONTCPSocketListenFailedExceptionName if a listener can't be established.
        
        NSLog(@"%@ raised during server init: %@", [localException name],
            localException);
        exit(1);
    }
    NS_ENDHANDLER
    
    do {
        ONTCPSocket *connectionSocket = nil;
        NSMutableData *clientData = nil;
        NSString *clientRequest = nil;
        NSAutoreleasePool *loopPool = nil;

        loopPool = [[NSAutoreleasePool alloc] init];
        NS_DURING {
            NSLog(@"Listening for the next connection");
            connectionSocket = [serverSocket acceptConnectionOnNewSocket];
            // Step 3.  Accept the incoming connection.
        }
        NS_HANDLER {
            // acceptConnectionOnNewSocket throws
            // ONTCPSocketAcceptFailedExceptionName if an error occurs.
            
            NSLog(@"%@ raised while waiting for connection: %@",
                [localException name], localException);
                
            break;
            // Break out of the server loop and exit.
        }
        NS_ENDHANDLER
        
        
        NS_DURING {
            // Step 4.  Interact with the client.
            
            clientData = [NSMutableData data];
            [clientData appendData:[connectionSocket readData]];
            while ( [connectionSocket isReadable] )
                [clientData appendData:[connectionSocket readData]];
            // Read in the client's request.  The readData: will block until the
            // client sends something.  It'll read up to 2048 bytes in one shot, although
            // you can adjust the size of the readbuffer with the setReadBufferSize: method.  
            // If the client's requests is larger than that, you have to loop until you've 
            // read the whole thing.  After the first read, loop and check the socket for
            // more data.  If there is some, keep readingl; otherwise, you're done.
                        
            clientRequest = [[[NSString alloc] initWithData:clientData
                                    encoding:NSASCIIStringEncoding] autorelease];
            // Convert the client's request into an ASCII string so that we can echo it back.
            
            [connectionSocket writeFormat:@"You sent me: %@", clientRequest];
            [connectionSocket writeFormat:@"I'm sending you this: %@", 
                                        [clientRequest uppercaseString]];
            // Generate a reply and send it back to the client.
            
            [loopPool release];
            // Step 5.  Close the connection and listening for the next connection.
            // Releasing the pool will release our socket, which causes it to be
            // gracefully closed.
        }
        NS_HANDLER {
            // ONTCPSocket's I/O methods can throw...
            // ONInternetSocketUserAbortExceptionName, if the connection is
            // 		aborted (via abortSocket:), or
            // ONInternetSocketWouldBlockExceptionName, if the socket is in non-blocking
            // 		mode and a read or write would have caused the socket to block.
            // ONInternetSocketReadFailedExceptionName if the read fails.
            // ONInternetSocketWriteFailedExceptionName if the write fails.
            
            NSLog(@"%@ raised during server interaction with client: %@",
                [localException name], localException);
                
            [loopPool release];
            
            break;
            // Exit the server loop on error.
        }
        NS_ENDHANDLER
        
    } while (1);
    
    [mainPool release];
    
    
    return(0);
    exit(0);
}